#ifndef MMPF_USBPCCAM_H
#define MMPF_USBPCCAM_H

#include "pcam_api.h"
#if defined(ALL_FW)
#include "mmps_dsc.h"
#endif
#include "mmpf_usb_h264.h"

#define EN_SMALL_TAG (0) //print small UART message for each stage. e.g. "H" for H.264 ENC done.

#if EN_SMALL_TAG
    #define DBG_SMALL_TAG(x) DBG_S(3, x)
#else
    #define DBG_SMALL_TAG(x)
#endif

#define USE_COMMON_H264_TYPE_CONDITION (1)

#define SUPPORT_S_FRAMEBASE (0) //support all other special frame-based features, which uses AIT payload header

typedef enum _H264_FORMAT_TYPE {
    INVALID_H264 = 0,
    FRAMEBASE_H264 = 0x81, // UVC frame-based single H.264 stream
    UVC_H264,   // H.264 in MJPEG, LGT mode
    SKYPE_H264, // H.264 + YUY2 in UVC YUY2
#if SUPPORT_S_FRAMEBASE
    FRAMEBASE_H264_YUY2,
    FRAMEBASE_H264_Y,
    FRAMEBASE_Y,
    FRAMEBASE_MJPEG_Y,
    FRAMEBASE_MJPEG_YUY2, 
    FRAMEBASE_NV12,
#endif
    UVC_H264_STREAM, // UVC1.1 H.264 payload. H.264 + YUY2 in MJPEG
    FRAMEBASE_DUAL_H264 = 0x86, // UVC frame-based dual H.264 streams
    FRAMEBASE_H264RT_MJPEG = 0x88,
    FRAMEBASE_H264FRAME_MJPEG = 0x89,
    FRAMEBASE_DUAL_H264_MJPEG = 0x8A, // UVC frame-based dual H.264 streams
    SKYPE_TX_STREAM
} H264_FORMAT_TYPE ;

typedef enum _PIPE_PATH {
    PIPE_0 = 0,
    PIPE_1,
    PIPE_2,
    PIPE_G,
#if SUPPORT_PIPE_Y
    PIPE_Y,
#endif
#if SUPPORT_AIT845x_MD
    PIPE_M,
#endif
    PIPE_AUD,
    PIPE_Error = 0xff
} PIPE_PATH ;

#define PIPE0_EN    (1<<0)
#define PIPE1_EN    (1<<1)
#define PIPE2_EN    (1<<2)
#define PIPEAUD_EN    (1<<3)
#define PIPE_EN(x)    (1<<x)
#define PIPE_EN_MASK (0x7)
#define PIPE_ASYNC   (1<<7) //enable this bit so that pipes could be sent via USB independently. e.g. MJPEG does not need to wait H.264
#define PIPE_EN_SYNC (1<<2) // not used for now

#if 0
//if mask == 7, NV12 will never be set.
//But no pipe set to 2 formats, bit operation is not needed.
#define PIPE_CFG_YUY2   (1<<0)
#define PIPE_CFG_MJPEG  (1<<1)
#define PIPE_CFG_H264   (1<<2)
#define PIPE_CFG_NV12   (1<<3)
#define PIPE_CFG_MASK   (0x7)
#define PIPE_CFG(pipe,cfg)  (  ( (cfg) & PIPE_CFG_MASK ) << ( pipe << 2 )  )
#else
//b0~b3 3 bits for format. b4 is for ring buffer tag.
#define PIPE_CFG_NONE   (0)
#define PIPE_CFG_YUY2   (1)
#define PIPE_CFG_MJPEG  (2)
#define PIPE_CFG_H264   (3)
#define PIPE_CFG_NV12   (4)
#define PIPE_CFG_MASK   (0x7) //format mask
#define PIPE_CFG_RING   (0x8)
#define PIPE_CFG(pipe,cfg)  (((cfg) & 0xF) << (pipe << 2))
#define GET_PIPE_CFG(pipe,cfg) ((cfg >> (pipe << 2)) & PIPE_CFG_MASK)
#define IS_PIPE_RING(stm_cfg_ptr,pipe) (stm_cfg_ptr->pipe_cfg & (PIPE_CFG(pipe,PIPE_CFG_RING)))
#define GET_PIPE_FORMAT(stm_cfg_ptr,pipe) ((stm_cfg_ptr->pipe_cfg >> (pipe << 2)) & PIPE_CFG_MASK)
#endif


#define PIPE_PH_TYPE_NA  (0)
#define PIPE_PH_TYPE_1  (1) ///<use UVC_H264_PH
#define PIPE_PH_TYPE_2  (2) ///<use SKYPE_H264_PH
#define PIPE_PH_TYPE_3  (3) ///<use FRAME_H264_PH
#define PIPE_PH_TYPE_4  (4) ///<use SKYPE_UVC_H264_PH //Windows Skype, UVC 1.1 H.264 payload.

typedef struct _STREAM_CTL
{
    MMP_ULONG  rd_index,wr_index ;
    MMP_ULONG  total_rd,total_wr ;
    MMP_ULONG  buf_addr,slot_size ;
    MMP_USHORT slot_num ;
#if ALIGN_PAYLOAD
    MMP_USHORT off_header ;
#endif    
} STREAM_CTL ;

#define STREAM_CFG_VER 2

#if STREAM_CFG_VER == 1
typedef struct _STREAM_CFG
{
    MMP_UBYTE pipe_en  ;
    MMP_UBYTE pipe_cfg ;   
    MMP_UBYTE pipe0_ph_type ;
    MMP_UBYTE pipe1_ph_type ;
    MMP_USHORT pipe0_w,pipe0_h;
    MMP_USHORT pipe1_w,pipe1_h;
    #if SUPPORT_PIPE_G
	MMP_USHORT pipeg_w,pipeg_h;
	#endif
    //MMP_USHORT pipe_sync ;
    STREAM_CTL pipe0_b ;
    STREAM_CTL pipe1_b ;
    #if SUPPORT_PIPE_G
    STREAM_CTL pipeg_b ;
    #endif
} STREAM_CFG ;
#define PIPE_INFO(pnum,field) (((STREAM_CFG*)stream_cfg)->pipe##pnum##_##field)

#elif STREAM_CFG_VER == 2
typedef struct _STREAM_CFG
{
    MMP_UBYTE  pipe_en;
    MMP_USHORT pipe_cfg;
    MMP_UBYTE  pipe0_ph_type;
    MMP_UBYTE  pipe1_ph_type;
    MMP_UBYTE  pipe2_ph_type;
} STREAM_CFG;

typedef struct _PIPE_INFO_TYPE
{
    MMP_USHORT w, h;
    MMP_BOOL bDirty; //set MMP_TRUE so that while using MMPF_Video_UpdateRdPtrByPayloadLength() it would update automatically
    STREAM_CTL b;
} PIPE_INFO_TYPE;

#define PIPE_INFO(pipe,field) usb_get_pipe(pipe)->field


#endif

/*
typedef enum {
    PCCAM_160_120  =0,
    PCCAM_176_144,
    PCCAM_320_240,
    PCCAM_352_288,
    PCCAM_640_480,
    PCCAM_720_480,
    PCCAM_800_600,
    PCCAM_1024_768,
    PCCAM_1280_720,
    PCCAM_1280_960,
    PCCAM_1600_1200,
    PCCAM_1920_1080,
    PCCAM_2048_1536,
    PCCAM_2560_1920,
    PCCAM_848_480 ,
    PCCAM_1280_1024,
    PCCAM_2560_1440,
    PCCAM_400_296,
    PCCAM_384_216,
    PCCAM_RES_NUM
//    PCCAM_RES_INVALID = 

} RES_TYPE_LIST ;
*/

typedef struct _RES_TYPE_CFG
{
    RES_TYPE_LIST res_type ;
    MMP_USHORT res_w ;
    MMP_USHORT res_h ;
        
} RES_TYPE_CFG ;

typedef struct _SKYPE_RES_TYPE_CFG
{
    SKYPE_RES_TYPE_LIST res_type ;
    MMP_USHORT res_w ;
    MMP_USHORT res_h ;
        
} SKYPE_RES_TYPE_CFG ;

#define DMA_SINGLE_BUFFER   0
#define DMA_DUAL_BUFFER     (1<<0)

//#define STREAM_DMA_BLK_VER 2 //Truman@120717 for code size
#define STREAM_DMA_BLK_VER 3 //Bossino@121205 for removing move dma and increase the list length of usb list

#if STREAM_DMA_BLK_VER == 1
typedef struct _STREAM_DMA_BLK
{
    MMP_USHORT max_dsize;
   // MMP_USHORT num_packets ;
   // MMP_USHORT last_packet_size;
    MMP_USHORT header_len ;
    MMP_ULONG  blk_addr ;
    MMP_ULONG  blk_size ;
    MMP_ULONG  next_blk ;
    MMP_ULONG  cur_addr   ;
    MMP_ULONG  tx_len ,tx_cnt    ;
    MMP_ULONG  dma_buf[2] ;
    MMP_ULONG  dummy_flag;
} STREAM_DMA_BLK ;
#else //STREAM_DMA_BLK_VER == 2 or STREAM_DMA_BLK_VER == 3
typedef struct _STREAM_DMA_BLK STREAM_DMA_BLK;
struct _STREAM_DMA_BLK
{
    MMP_USHORT flag; //d4~d6: source, d2~d3 pipe, d1:IBC ring, d0:dummy
    #define DUMMY_DMA_BLK (1 << 0) //skip moving for this DMA block
    //#define DMA_BLK_RING (1 << 1) //any YUY2, Y_only or other 1 buffer ring from IBC
    #define DMA_BLK_RING (1 << 1) //any ring buffer. Refer source bits (d4~d7) while processing this.
    #define PIPE0_RING ((0 << 2) | DMA_BLK_RING) //any 1 buffer ring from IBC0
    #define PIPE1_RING ((1 << 2) | DMA_BLK_RING) //any 1 buffer ring from IBC1
    #define PIPE2_RING ((2 << 2) | DMA_BLK_RING) //any 1 buffer ring from IBC2
    #define GET_DMA_BLK_PIPE(x) ((x >> 2) & 0x3)
    #define DMA_BLK_SRC_BUF  (0 << 4) //any frame which is ready to transfer in the memory.
    #define DMA_BLK_SRC_IBC  (1 << 4) //the data is generated from IBC and need to monitor the output of IBC.
    #define DMA_BLK_SRC_H264 (2 << 4) //not implemented yet. the data is generated from H.264 encoder and need to monitor the output of H.264 encoder.
    #define DMA_BLK_SRC_JPEG (3 << 4) //not implemented yet. the data is generated from JPEG and need to monitor the output of JPEG.
    #define DMA_BLK_SRC_CPU  (4 << 4) //not implemented yet. the data is generated from CPU algorithm and need to monitor the output of CPU.
    #define GET_DMA_BLK_SRC(x) (x & 0x70)
    //d7 reserved
    #define DMA_BLK_PAUSE  (0x100) //d8. not used yet. pause the source
    #define DMA_BLK_RESUME (0x200) //d9. not used yet. resume the source
    MMP_USHORT  header_len;
    #define DMA_BLK_JPEG_HDR_LEN (4) //special length which dealing with APP tags of JPEG format
    MMP_ULONG  blk_addr;
    MMP_ULONG  blk_size;
    STREAM_DMA_BLK *next_blk;
};

#endif


void usb_set_mjpg_quality(MMP_ULONG quality);
RES_TYPE_CFG *GetResCfg(MMP_UBYTE resolution);
extern const RES_TYPE_CFG *gPCCamCurRes;
RES_TYPE_LIST GetSkypeMainResIndex(MMP_USHORT width, MMP_USHORT height);
RES_TYPE_LIST GetSkypePreviewResIndex(MMP_USHORT width, MMP_USHORT height);
SKYPE_RES_TYPE_CFG *GetSkypeResCfg(MMP_UBYTE resolution);
SKYPE_RES_TYPE_CFG *SearchSkypeMainStreamRes(MMP_USHORT width, MMP_USHORT height);
SKYPE_RES_TYPE_CFG *SearchSkypePreviewStreamRes(MMP_USHORT width, MMP_USHORT height);
//MMP_USHORT MMPF_PollingPCCam(void);
MMP_ULONG GetYUY2FrameSize(MMP_UBYTE resolution);
#if CAPTURE_RAW_XU
typedef enum _RAW_CAPTURE_FORMAT {
    RAW_CAPTURE_NONE,
    RAW_CAPTURE_AUTO,
    RAW_CAPTURE_BAY8,
    RAW_CAPTURE_YUY2,
    RAW_CAPTURE_BAY10
} RAW_CAPTURE_FORMAT;
extern RAW_CAPTURE_FORMAT gRawFormat;
MMP_ULONG GetRawFrameSize(RAW_CAPTURE_FORMAT raw_format);
#else
MMP_ULONG GetRawFrameSize(MMP_UBYTE resolution);
#endif
MMP_ULONG GetMJPEGFrameSize(MMP_UBYTE resolution);
MMP_ULONG GetYUV420FrameSize(MMP_UBYTE resolution);
MMP_USHORT GetMaxFrameRate(MMP_UBYTE resolution);
MMP_ULONG GetH264FrameSize(MMP_UBYTE resolution);
MMP_USHORT GetZoomResolution(void);
#if defined(ALL_FW)
MMP_ERR SetPreviewZoom(MMPS_DSC_ZOOM_DIRECTION zoomdir);
MMP_ERR SetVideoZoomByJump(PCAM_USB_ZOOM *zoomInfo, MMP_BOOL AsyncMode);
MMP_ERR SetVideoPanTiltByJump(PCAM_USB_PANTILT *panInfo, MMP_BOOL AsyncMode);
#endif

//void MMPF_InitPCCam(void);
void MMPF_InitPCCam(const STREAM_CFG *stream_cfg,MMP_ULONG res);
//void InitScale(void);
MMPF_SCALER_FIT_RANGE *GetFitRange(void);
void InitScale(const STREAM_CFG *stream_cfg,MMP_ULONG res);
const STREAM_CFG * usb_get_stream_cfg(void);
#if STREAM_CFG_VER >= 2
//PIPE_INFO_TYPE* usb_get_pipe(const MMP_BYTE pipe);
extern PIPE_INFO_TYPE gPipeInfo[3+SUPPORT_PIPE_G+SUPPORT_PIPE_Y+SUPPORT_AIT845x_MD+SUPPORT_MP4];
__inline PIPE_INFO_TYPE* usb_get_pipe(const MMP_BYTE pipe)
{
    return &gPipeInfo[pipe];
}
#endif
void usb_set_stream_cfg(const STREAM_CFG * cur_pipe);
MMP_USHORT MMPF_GetJfifTag(MMP_USHORT *pTagID, MMP_USHORT *pTagLength, MMP_UBYTE **ppImgAddr, MMP_ULONG length);

void MMPF_Video_GetBufBound(PIPE_PATH pipe,MMP_ULONG *lowbound,MMP_ULONG *highbound);
void MMPF_Video_GetSlotInfo(MMP_BOOL ring_buf_mode,PIPE_PATH pipe,MMP_ULONG *addr,MMP_ULONG *slot_size,MMP_USHORT *slot_num);

MMP_ULONG MMPF_Video_GetEmptySlotNum(PIPE_PATH pipe);
MMP_BOOL MMPF_Video_IsEmpty(PIPE_PATH pipe);
MMP_BOOL MMPF_Video_IsFull(PIPE_PATH pipe);
MMP_BOOL MMPF_Video_IsFullReviewed(PIPE_PATH pipe);
MMP_BOOL MMPF_Video_CheckReSend(PIPE_PATH pipe);
void MMPF_Video_UpdateWrPtr(PIPE_PATH pipe);
void MMPF_Video_UpdateRdPtr(PIPE_PATH pipe);
MMP_UBYTE *MMPF_Video_CurRdPtr(PIPE_PATH pipe);
MMP_UBYTE *MMPF_Video_CurWrPtr(PIPE_PATH pipe);
void MMPF_Video_Init_Buffer(MMP_USHORT off_header);
STREAM_CTL *MMPF_Video_GetStreamCtlInfo(PIPE_PATH pipe);
void MMPF_Video_UpdateRdPtrByPayloadLength(MMP_BOOL bUseDirty);

void MMPF_Video_InitDMABlk(MMP_USHORT uvc_payload_size,MMP_ULONG dmabuf1,MMP_ULONG dmabuf2) ;
void MMPF_Video_AddDMABlk(MMP_USHORT header_len,MMP_ULONG blk_addr,MMP_ULONG blk_size,MMP_USHORT flag);
STREAM_DMA_BLK *MMPF_Video_GetCurBlk(void);
MMP_BOOL MMPF_Video_GoNextBlk(void);
MMP_UBYTE *MMPF_Video_GetBlkAppHeader(STREAM_DMA_BLK *dma_blk);
void MMPF_USB_AudioDm(void);
void MMPF_USB_ReleaseDm(MMP_UBYTE pipe);

void MMPF_SetPCCAMCompressBuf(unsigned int buf_addr, unsigned int buf_size);
void MMPF_SetPCCAMLineBuf(unsigned int buf_addr);
void PCCamSetQctl(MMP_ULONG qctl);
void ChangeMJEEG_SnapshotResolution(MMP_UBYTE res, MMP_BOOL jpeg_cap);
void ChangeMJEEG_PreviewResolution(MMP_UBYTE res, MMP_BOOL jpeg_cap);
void MJPEG_InitPCCam(const STREAM_CFG *stream_cfg, MMP_ULONG res, MMP_BOOL jpeg_cap);
void MJPEG_InitScale(const STREAM_CFG *stream_cfg);
void MJPGFrameRateCtlInitial(void);
MMP_UBYTE CheckMJPGFrameDrop(void);
void usb_set_mjpg_quality_level(MMP_UBYTE level);

#endif
